import { Injectable, SystemJsNgModuleLoader } from "@angular/core";
import { FileService } from "./file.service";
import {
  MonacoLanguageClient,
  CloseAction,
  ErrorAction,
  MonacoServices,
  createConnection
} from "monaco-languageclient";
import { listen, MessageConnection } from "vscode-ws-jsonrpc";
const ReconnectingWebSocket = require("reconnecting-websocket");
import { LSPMessageFactory } from "../LSP/LSPMessageFactory";
import { LSPFrameFactory } from "../LSP/LSPFrameFactory";
import { WebSocketService } from "./web-socket.service";
import { RecordTimeService } from "../record-time.service";

@Injectable({
  providedIn: "root"
})
export class TextService {
  messageId;
  projectAddress;
  version;
  ws: WebSocket;
  editor: monaco.editor.IStandaloneCodeEditor;
  interval;
  newValue;
  noError;
  connection: MessageConnection;
  languageClient: MonacoLanguageClient;
  filename: string;
  hasSpace = false;
  lastVersion: string;
  isOpen = false;
  saveTimes: number;
  timeFlag = 0;
  frame: string = "";
  receiveFrame: boolean;
  constructor(
    private wsService: WebSocketService,
    private fileService: FileService,
    private recordTimeService: RecordTimeService,
  ) {
    this.messageId = 0;
    this.openWebSocket();
  }

  languageId = "safenl";
  hasCreatedEditor = false;
  createEditor(title, version, code) {
    var that = this;
    if (!this.hasCreatedEditor) {
      this.hasCreatedEditor = true;
      //create model
      this.filename = title + version + ".safenl";
      
      let model = monaco.editor.createModel(
        code,
        "safenl",
        monaco.Uri.parse(
          "file://root/safenl-language-server/test-data/" + this.filename + ".safenl"
        )
      );
      
      this.editor.setModel(model);

      var rootUri = "file://root/safenl-language-server/test.safenl";
      //console.log(this.editor)
      // install Monaco language client services
      MonacoServices.install(this.editor, { rootUri: rootUri });
      // create the web socket
      const url = "ws://localhost:8085/sampleServer";
      const webSocket = this.createWebSocket(url);

      // listen when the web socket is opened
      listen({
        webSocket,
        onConnection: (connection: MessageConnection) => {
          // create and start the language client
          const languageClient = this.createLanguageClient(connection);
          const disposable = languageClient.start();
          connection.onClose(() => disposable.dispose());
          that.connection = connection;
          that.languageClient = languageClient;
        }
      });
    }
    this.editor.setValue(code);
    //listen when the editor's value changed
    this.interval = setInterval(function() {
      that.didSave();
    }, 1000);
  }

  public createLanguageClient(
    connection: MessageConnection
  ): MonacoLanguageClient {
    return new MonacoLanguageClient({
      name: `Safenl Client`,
      clientOptions: {
        // use a language id as a document selector
        documentSelector: ["safenl"],
        // disable the default error handler
        errorHandler: {
          error: () => ErrorAction.Continue,
          closed: () => CloseAction.DoNotRestart
        }
      },
      // create a language client connection from the JSON RPC connection on demand
      connectionProvider: {
        get: (errorHandler, closeHandler) => {
          return Promise.resolve(
            createConnection(<any>connection, errorHandler, closeHandler)
          );
        }
      }
    });
  }
  public createWebSocket(url) {
    var socketOptions = {
      maxReconnectionDelay: 10000,
      minReconnectionDelay: 1000,
      reconnectionDelayGrowFactor: 1.3,
      connectionTimeout: 10000,
      maxRetries: Infinity,
      debug: false
    };
    return new ReconnectingWebSocket(url, undefined, socketOptions);
  }
  getText() {
    return this.editor.getValue();
  }

  //============================== websocket new ==============================
  openWebSocket() {
    this.ws = new WebSocket("ws://localhost8/TextLSP");
    var that = this;
    this.ws.onopen = function() {
      //console.log('client: ws connection is open');
      // that.ws.send('hello');
    };
    this.ws.onmessage = function(e) {
      var message = JSON.parse(e.data);
      that.frame += message.frame;
      let lastFrame = message.lastFrame;
      if (!lastFrame) {
        return;
      }
      message = JSON.parse(that.frame);
      that.frame = "";
      let params = message.params;
      that.lastVersion = params.textDocument.lastVersion;
      if (message.method == "TextDocument/didChange") {
        console.log(
          " ============================== text receive didChange message==============================\n"
        );
        console.log(message);
        that.update(message);
        let T0 =
          params.textDocument.T0 == undefined ? 0 : params.textDocument.T0;
        let T1 =
          params.textDocument.T1 == undefined ? 0 : params.textDocument.T1;
        let T2 =
          params.textDocument.T2 == undefined ? 0 : params.textDocument.T2;
        let T3 =
          params.textDocument.T3 == undefined ? 0 : params.textDocument.T3;
        let T4 = new Date().getTime();
        let Flag1 =
          params.textDocument.Flag1 == undefined
            ? 0
            : params.textDocument.Flag1;
        let Flag2 =
          params.textDocument.Flag2 == undefined
            ? 0
            : params.textDocument.Flag2;
        let Flag3 =
          params.textDocument.Flag3 == undefined
            ? 0
            : params.textDocument.Flag3;
        that.sendTimeRecoder(
          that.lastVersion,
          T0,
          T1,
          T2,
          T3,
          T4,
          Flag1,
          Flag2,
          Flag3
        );
      } else if (message.method == "TextDocument/registered") {
        console.log(
          " ============================== text receive registered message==============================\n"
        );
        console.log(message);
        that.registed_new(message);
      } else if (message.method == "TextDocument/changeLastVersion") {
        console.log(
          " ============================== text receive changeLastVersion message==============================\n"
        );
        console.log(message);
      }
    };
    this.ws.onerror = function(e) {
      //console.log('==============================error==============================', e);
    };
    this.ws.onclose = function(e) {
      console.log(
        "==============================close==============================",
        e
      );
      that.isOpen = false;
    };
  }

  //==============================发送消息 new ==============================
  sendTimeRecoder(lastVersion, T0, T1, T2, T3, T4, Flag1, Flag2, Flag3) {
    let timeRecoder = {
      uri: this.projectAddress + this.version,
      lastVersion: lastVersion,
      T0: T0,
      T1: T1,
      T2: T2,
      T3: T3,
      T4: T4,
      Flag1: Flag1,
      Flag2: Flag2,
      Flag3: Flag3
    };
    // let frame = LSPFrameFactory.getFrame("TextDocument/TimeRecoder", timeRecoder)
    // console.log("============================== TextDocument TimeRecoder ==============================")
    // // console.log(timeRecoder)
    // this.sendMessage(frame)
    let message = LSPMessageFactory.getMessageNoFraming(
      this.messageId++,
      "TextDocument/TimeRecoder",
      timeRecoder
    );
    this.sendMessage2(this.ws, message);
  }

  register(title, version: string, text: string) {
    // version =
    //   version == undefined
    //     ? "undefined"
    //     : version.replace(":", "_").replace(":", "_");
    // console.log(version);
    // let textDocument = {
    //   uri: title + version,
    //   version: 0,
    //   text: text
    // };
    // let params = {
    //   textDocument: textDocument
    // };
    // // let frame = LSPFrameFactory.getFrame("TextDocument/didOpen", params)
    // // console.log("============================== TextDocument TimeRecoder ==============================")
    // // this.sendMessage(frame)
    // let message = LSPMessageFactory.getMessageNoFraming(
    //   this.messageId++,
    //   "TextDocument/didOpen",
    //   params
    // );
    // this.sendMessage2(this.ws, message);
    this.projectAddress = title;
    this.version = version;
    this.newValue = text;
    //createEditor
    this.createEditor(this.projectAddress, this.version, text);
    // let that = this;
    // this.editor.addCommand(
    //   monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S,
    //   function() {
    //     that.didSave();
    //   },
    //   ""
    // );
  }

  register_new(title: string, version: string) {
    version = version.replace(":", "_").replace(":", "_");
    let textDocument = {
      uri: title + version,
      projectAddress: title,
      projectVersion: version,
      version: 0
    };
    let params = {
      textDocument: textDocument
    };
    // let frame = LSPFrameFactory.getFrame("TextDocument/register", params)
    // this.sendMessage(frame)
    let message = LSPMessageFactory.getMessageNoFraming(
      this.messageId++,
      "TextDocument/register",
      params
    );
    this.sendMessage2(this.ws, message);
    this.projectAddress = title;
    this.version = version;
  }
  registed_new(message) {
    this.isOpen = true;
    let params = message.params;
    this.newValue = params.text;
    //createEditor
    this.createEditor(this.projectAddress, this.version, this.newValue);
  }
  sendMessage(frame) {
    this.sendMessage1(this.ws, this.messageId++, frame);
  }

  sendMessage1(ws, messageId, frame) {
    let sframe = JSON.stringify(frame);
    let message;
    let i = 0;
    let frameId = 0;
    let that = this;
    let interval1 = setInterval(function() {
      if (i > sframe.length) {
        clearInterval(interval1);
      } else {
        if (sframe.length > i + 510000) {
          message = LSPMessageFactory.getLSPMessage(
            messageId,
            frameId,
            false,
            sframe.substring(i, i + 510000)
          );
        } else {
          message = LSPMessageFactory.getLSPMessage(
            messageId,
            frameId,
            true,
            sframe.substring(i)
          );
        }
        if (ws.readyState == WebSocket.OPEN) {
          console.log(message);
          ws.send(JSON.stringify(message));
          i += 510000;
          frameId++;
        } else if (ws.readyState == WebSocket.CLOSED) {
          console.log("diagram.ws.readyState=CLOSED ", ws.readyState);
        } else if (ws.readyState == WebSocket.CLOSING) {
          console.log("diagram.ws.readyState=CLOSING ", ws.readyState);
        } else if (ws.readyState == WebSocket.CONNECTING) {
          console.log("diagram.ws.readyState=CONNECTING ", ws.readyState);
        }
      }
    }, 1000);
  }

  //没有分包，直接发送message
  sendMessage2(ws, message) {
    let i = 0;
    let that = this;
    let interval1 = setInterval(function() {
      if (ws.readyState == WebSocket.OPEN) {
        clearInterval(interval1);
        console.log(message);
        ws.send(JSON.stringify(message));
      } else if (ws.readyState == WebSocket.CLOSED) {
        console.log("diagram.ws.readyState=CLOSED ", ws.readyState);
      } else if (ws.readyState == WebSocket.CLOSING) {
        console.log("diagram.ws.readyState=CLOSING ", ws.readyState);
      } else if (ws.readyState == WebSocket.CONNECTING) {
        console.log("diagram.ws.readyState=CONNECTING ", ws.readyState);
      }
    }, 1000);
  }
  unregister() {
    let textDocument = {
      uri: this.projectAddress + this.version,
      languageId: "safenl",
      version: 0,
      text: null
    };
    let params = {
      textDocument: textDocument
    };
    let message = LSPFrameFactory.getFrame("TextDocument/didClose", params);
    this.ws.send(JSON.stringify(message));
  }

  //new
  public didSave(): void {
    let markers = monaco.editor.getModelMarkers({});
    let error = false;
    if (markers.length > 0) error = true;
    let value = this.editor.getValue();
    if (
      (value != this.newValue || this.saveTimes == 4) &&
      !error &&
      this.isOpen
    ) {
      // console.log("this.editor.getValue()\n"+value)
      // console.log("last version Value\n"+this.newValue)
      let params = {
        textDocument: {
          uri: this.filename.split(".")[0],
          version: null,
          lastVersion: this.lastVersion,
          changeTime: new Date().getTime()
        },
        text: value
      };
      //向服务器发送最新版
      // let frame = LSPFrameFactory.getFrame("TextDocument/didSave", params)
      // this.sendMessage(frame)
      let message = LSPMessageFactory.getMessageNoFraming(
        this.messageId++,
        "TextDocument/didSave",
        params
      );
      this.sendMessage2(this.ws, message);
      this.newValue = value;
    }
    this.saveTimes = (this.saveTimes + 1) % 5;
  }

  //==============================接收消息 new==============================
  update(jsonMessage) {
    let params = jsonMessage.params;
    this.newValue = params.text;
    var position = this.editor.getPosition();
    this.editor.setValue(this.newValue);
    this.editor.setPosition(position);
  }
}
